home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1997 / MacHack 1997.toast / Hacks / Hacks ’96 / Venus / Dialog.cc next >
Text File  |  1996-06-22  |  12KB  |  314 lines

  1. /*
  2.  ***********************************************************************
  3.  *
  4.  *
  5.  *                            Generic Dialog Class
  6.  *                    Implementing a few very basic functions
  7.  *                        messing with items, etc
  8.  *        
  9.  *
  10.  ***********************************************************************
  11.  */
  12.  
  13. #include "Dialog.h"
  14. #include "mymenv.h"
  15.  
  16.  
  17. /*
  18.  *-------------------------------------------------------------------------------------
  19.  *                            Dialog construction/destruction
  20.  *
  21.  * Dialog constructors create a (modeless) dialog (from a DLOG resource) and set
  22.  * up the initial state of the controls and user items.
  23.  * Note that the dialog is ought to be specified as initially hidden
  24.  * in the resource template. So that constructors of the derived class could get a chance
  25.  * to set up user item drawing routines, or initialize/move around custom controls.
  26.  * Call show() when ready (from an upper-level constructor)
  27.  */
  28.  
  29.                             // Create a color dialog from a resource template
  30. ModelessDialog::ModelessDialog(const short resource_id)
  31. {
  32.   assert( this_window = (WindowPtr)GetNewDialog(resource_id, nil, (WindowPtr)-1) );
  33.   SetWRefCon(this_window,(long)this);        // Store the ptr to our class in RefCon
  34. }
  35.  
  36.  
  37.                             // A kludge for a virtual destructor
  38. void ModelessDialog::destroy_it(void)
  39. {
  40.   assert( this_window != 0 );
  41.   DisposeDialog((DialogPtr)this_window);
  42.   this_window = 0;
  43. }
  44.  
  45. /*
  46.  *-------------------------------------------------------------------------------------
  47.  *                                    Event Handling
  48.  */
  49.  
  50. Boolean ModelessDialog::handle_event(const EventRecord& the_event)
  51. {
  52.   if( !IsDialogEvent(&the_event) )
  53.     return ScreenWindow::handle_event(the_event);    // Not a dialog event, let ScreenWindow
  54.                                                     // handle that. Note that, say, dragging
  55.                                                     // of a window is reported as NOT a dialog
  56.                                                     // event
  57.     
  58.   if( the_event.what == activateEvt && (WindowPtr)(the_event.message) == our_window() )
  59.     handle_activate(the_event.modifiers & activeFlag);
  60.  
  61.   DialogPtr dialog_hit;
  62.   short item_hit;
  63.   if( !DialogSelect(&the_event, &dialog_hit, &item_hit) )
  64.     return TRUE;                            // assume that DialogSelect has handled the Event
  65.   if( dialog_hit != this_window )
  66.     return TRUE;                            // Not our dialog event, wait for the other event
  67.  
  68.                                             // The following stmt means that if a generic_item_hit
  69.                                             // handle didn't handle the click completely, a more
  70.                                             // specific handle would be given a chance
  71.   return generic_item_hit(item_hit) || handle_item_hit(item_hit);
  72. }
  73.  
  74. Boolean ModelessDialog::handle_null_event(const long event_time)
  75. {
  76.   return TRUE;
  77. }
  78.  
  79.                                             // Handle suspend/resume events
  80. Boolean ModelessDialog::handle_activate(Boolean onoff)
  81. {
  82.   return TRUE;
  83. }
  84.  
  85.                                 // Figure out if our item was hit and give an object
  86.                                 // click handler a chance to handle the click.
  87.                                 // Return TRUE if the click is _completely_
  88.                                   // handled, return FALSE if additional attention
  89.                                   // needed (say, a control's value was changed)
  90. Boolean ModelessDialog::generic_item_hit(const int item_no)
  91. {
  92.   Handle        item_handle;
  93.   short            item_type;
  94.   Rect            item_rect;
  95.   GetDItem((DialogPtr)this_window, item_no, &item_type, &item_handle, &item_rect);
  96.   if( (item_type & ctrlItem) != ctrlItem || item_handle == nil )
  97.     return FALSE;
  98.  
  99.   BasicControl * our_control_ptr = (BasicControl *)GetControlReference((ControlHandle)item_handle);
  100.   
  101.   return our_control_ptr != nil && our_control_ptr->is_our_control() && 
  102.            our_control_ptr->handle_click();
  103. }
  104.  
  105. /*
  106.  *-------------------------------------------------------------------------------------
  107.  *                            Utilities to handle dialog items
  108.  */
  109.  
  110.  
  111.  
  112.                                 // Take a "ref" of a dialog item
  113. ModelessDialog::Item::Item(const ModelessDialog& the_dialog, const int item_no)
  114. {
  115.   GetDItem((DialogPtr)the_dialog.our_window(), item_no, &item_type, &item_handle, &item_rect);
  116.   assert( item_handle != nil );
  117. }
  118.  
  119.  
  120.                                 // Take a "ref" of a dialog _control_ item, and
  121.                                 // make sure that it's a control indeed
  122. ModelessDialog::ControlItem::ControlItem(const ModelessDialog& the_dialog, const int item_no)
  123.     : ModelessDialog::Item(the_dialog,item_no)
  124. {
  125.   assert( (item_type & ctrlItem) == ctrlItem );
  126. }
  127.  
  128.                                 // Take a "ref" of a dialog _text_ item, and
  129.                                 // make sure that it's text indeed
  130. ModelessDialog::TextItem::TextItem(const ModelessDialog& the_dialog, const int item_no)
  131.     : ModelessDialog::Item(the_dialog,item_no)
  132. {
  133.   assert( (item_type & statText) == statText );
  134. }
  135.  
  136.                                             // Draw a new text in an item
  137. void ModelessDialog::TextItem::draw(const Str255 str)
  138. {
  139.   SetDialogItemText(item_handle,str);
  140. }
  141.  
  142.                                 // Make a TextItem when the text is an ASCII representation
  143.                                 // of a number
  144. ModelessDialog::NumberItem::NumberItem(const ModelessDialog& the_dialog, const int item_no)
  145.     : ModelessDialog::TextItem(the_dialog,item_no)
  146. {
  147. }
  148.  
  149.                                         // Draw a new number in an item
  150. void ModelessDialog::NumberItem::draw(const int number)
  151. {
  152.   Str255 temp_str;
  153.   NumToString(number,temp_str);
  154.   TextItem::draw(temp_str);
  155. }
  156.  
  157.  
  158.  
  159.                                     // Set a new value of a control, clipped to
  160.                                       // its [min,max] range
  161. void BasicControl::set_value(const int new_value)
  162. {
  163.   ControlHandle ctl_handle = our_control();
  164.   const int max_val = GetControlMaximum(ctl_handle);
  165.   SetControlValue(ctl_handle, new_value > max_val ? max_val :
  166.                                 new_value < GetControlMinimum(ctl_handle) ?
  167.                                 GetControlMinimum(ctl_handle) : new_value );
  168. }
  169.  
  170. /*
  171.  *-------------------------------------------------------------------------------------
  172.  *                                    Patching the CDEF
  173.  *
  174.  * In handling a scrollbar, we need to tell the Control Manager how to change the control
  175.  * value when PgUp/PgDn/ArrowUp/ArrowDn areas of a scrollbar got clicked. Normally it's
  176.  * accomplished by a track Action procedure, which is called by TrackControl(). In our
  177.  * case, TrackControl() is called by DialogSelect. We could supply our Action procedure
  178.  * by putting a pointer to it in a corresponding field of the ControlRecord. But it
  179.  * doesn't work! The reason is that if an action procedure is specified this way, it's
  180.  * called under two different circumstances: mouse click in PgUp/PgDn/ArrowUp/ArrowDn areas,
  181.  * and a mouse click in the thumb (indicator). In the former case, the action procedure is
  182.  * passed two parameters, in the latter case, no parameters. Since the action procedure
  183.  * is a 'pascal' one, passing it zero parameters when it expects two, or vice versa
  184.  * messes up the stack on return (with all the disastorous consequences). As IM itself
  185.  * admits, the only way to specify a "generic action procedure" is to modify a CDEF,
  186.  * which we're going to do.
  187.  * Note, the usual way of handling this situation is calling FindControl(), TrackControl()
  188.  * etc before DialogSelect(). In a sense, the usual hack is to duplicate DialogSelect.
  189.  * IMHO, patching CDEF seems more logical.
  190.  *
  191.  * Note, in patching a CDEF, we assume that BasicControl is a scrollbar-type thing,
  192.  * that's why real_defproc_handle, patch_defproc_handle etc are declared static.
  193.  * Although it's damn easy to lift this limitation.
  194.  *
  195.  * Note, that in installing our patch, we can't just barge into CDEF handle and change
  196.  * the pointer. ResourceManager, who created that CDEF handle, wouldn't like that.
  197.  * So we need to allocate our own handle, stash a pointer (universal pointer) of our patch
  198.  * into it, and put the new handle into CDEF. DisposeControl would dispose of that handle
  199.  * later.
  200.  */
  201.                                     // Late constructor for a basic control
  202.                                     // A regular constructor won't cut it, since it would
  203.                                     // be called before the container (the dialog)
  204.                                     // gets constructed
  205. void BasicControl::bind(const ModelessDialog::ControlItem& item)
  206. {
  207.   this_control = (ControlHandle)item;
  208.   SetControlReference(this_control,(long)this);
  209.   // SetControlAction(this_control,track_action_relay_upp);
  210.   // SetControlAction(this_control,(ControlActionUPP)(-1));
  211.   if( real_defproc_handle == nil )        // save the old CDEF handle and initialize a new one
  212.   {
  213.     real_defproc_handle = (ControlDefProcPtr *)(*this_control)->contrlDefProc;
  214.     patch_defproc_handle = (ControlDefProcPtr *)NewHandle(sizeof(ControlDefUPP));
  215.     HLock((Handle)patch_defproc_handle);
  216.     *patch_defproc_handle = (ControlDefProcPtr)defproc_patch_upp;
  217.   }                                        // Install our CDEF patch
  218.   assert( real_defproc_handle == (ControlDefProcPtr *)(*this_control)->contrlDefProc );
  219.   (*this_control)->contrlDefProc = (Handle)patch_defproc_handle;
  220.   
  221. }
  222.  
  223.  
  224.                         // This is an on-the-fly patch to a scroll bar control definition
  225.                         // function.
  226.                         // The patch itself calls the standard CDEF, and, if it finds out
  227.                         // that thumb had been moved and/or gray areas/arrows were clicked,
  228.                         // calls the object's track action procedure
  229.                         // We assume that the pointer to the BasicControl Object is stored
  230.                         // in the refcon
  231. pascal SInt32 BasicControl::defproc_patch
  232.     (SInt16 var_code, ControlRef the_control, ControlDefProcMessage message, SInt32 param)
  233. {
  234.   
  235.   SInt32 def_result = CallControlDefProc(*real_defproc_handle,var_code,the_control,message,param);
  236.   if( (message == drawCntl && (short)param == 129)     // indicator to be redrawn
  237.      || (message == testCntl && def_result != 0) )    // a scrollbar area might've been clicked
  238.   if( !StillDown() )                    // mouse was released, that is, the click is completed
  239.   {
  240.     BasicControl * this_ptr = (BasicControl *)((*the_control)->contrlRfCon);
  241.     assert( this_ptr != nil && this_ptr->is_our_control() );
  242.     this_ptr->track_action(def_result);
  243.   }
  244.   return def_result;
  245. }
  246.  
  247. ControlDefProcPtr * BasicControl::real_defproc_handle = 0; 
  248. ControlDefProcPtr * BasicControl::patch_defproc_handle = 0; 
  249. ControlDefUPP BasicControl::defproc_patch_upp 
  250.     = NewControlDefProc(defproc_patch);    // Universal pointer to BasicControl::defproc_patch
  251.  
  252. #if 0
  253.                         // This is a relay between the Control Manager and real action
  254.                         // tracking procedure
  255.                         // We assume that the pointer to the BasicControl Object is stored
  256.                         // in the refcon
  257. pascal void BasicControl::track_action_relay(ControlHandle the_control, unsigned short part_no)
  258. {
  259.   DebugStr("\pIn track action relay");
  260.   if( part_no >= inThumb )                    // Note when the mouse is clicked in the indicator,
  261.     return;                                    // parameters are probably invalid
  262.   BasicControl * this_ptr = (BasicControl *)GetControlReference(the_control);
  263.   assert( this_ptr != nil && this_ptr->is_our_control() );
  264.   this_ptr->track_action(part_no);
  265. }
  266.  
  267. ControlActionUPP BasicControl::track_action_relay_upp 
  268.     = NewControlActionProc(track_action_relay);    // Universal pointer to the user_item_univ_handler
  269.  
  270. #endif
  271.  
  272. /*
  273.  *-------------------------------------------------------------------------------------
  274.  *                                    Handling User items
  275.  */
  276.  
  277.                         // This is a relay between the Dialog Manager and a real user item
  278.                         // drawing procedure
  279.                         // We assume that the pointer to the Dialog Object is stored
  280.                         // in the refcon
  281. pascal void UserItem::user_item_relay_handler(WindowPtr the_dialog, short the_item)
  282. {
  283.   ModelessDialog * this_ptr = (ModelessDialog *)GetWRefCon(the_dialog);
  284.   assert( this_ptr != nil );
  285.   this_ptr->draw_user_item(the_item);
  286. }
  287.  
  288. UserItemUPP UserItem::user_item_relay_handler_upp 
  289.     = NewUserItemProc(user_item_relay_handler);    // Universal pointer to the user_item_univ_handler
  290.  
  291.  
  292.                         // Binding and activating a user item
  293.                         // It sets up a relay function as an item handle
  294. void UserItem::bind(const ModelessDialog& the_dialog, const int item_no)
  295. {
  296.   Handle        item_handle;
  297.   short            item_type;
  298.   GetDItem((DialogPtr)the_dialog.our_window(), item_no, &item_type, &item_handle, &rect);
  299.   assert( item_handle == nil );                    // make sure it hasn't been set up yet
  300.   SetDItem((DialogPtr)the_dialog.our_window(), item_no, item_type, (Handle)user_item_relay_handler_upp, &rect);
  301. }
  302.  
  303. #if 0
  304.                                 // Standard way to draw a border around the default button.
  305. void ModelessDialog::draw_default_item_border(const int item_no)
  306. {
  307.   const Item item(*this,item_no);
  308.     
  309.   PenSize(3,3);
  310.   InsetRect((Rect *)(const Rect *)item, -4, -4);
  311.   FrameRoundRect((const Rect *)item, 16,16);
  312. }
  313. #endif
  314.